/*
 * nxt.h
 *
 *  Created on: Mar 14, 2012
 *  	Author: Victor Nidens
 *
 *  This file is part of NXT USB.
 *
 *	NXT USB is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *	NXT USB library is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with NXT USB. If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef NXT_H_
#define NXT_H_

#include "nxtconst.h"
#include "libusb-1.0/libusb.h"

/**
 * Struct for storing parsed motor state from the result of nxtGetMotorState request
 */
#pragma pack(push, 1)
typedef struct {
	uint16	rval;
	uint8	status;
	uint8	port;
	int8	power;
	uint8	mode;
	uint8	regulation;
	int8	turn_ratio;
	uint8	runstate;
	uint32	tacho_limit;
	int32	tacho_count;
	int32	block_tacho_count;
	int32	rotation_count;
} MotorOutputState;
#pragma pack(pop)

/**
 * Struct for storing parsed sensor state
 */
#pragma pack(push, 1)
typedef struct {
	uint16	rval;
	uint8	status;
	uint8	port;
	uint8	valid;
	uint8	calibrated;
	uint8	sensor_type;
	uint16	sensor_mode;
	uint16	raw_value;
	uint16	normal_value;
	uint16	scaled_value;
} SensorState;
#pragma pack(pop)

/**
 * Struct for storing parsed battery state from response on nxtGetBatteryLevel
 */
#pragma pack(push, 1)
typedef struct {
	uint16	rval;
	uint8	status;
	uint16	batteryState;
} BatteryState;
#pragma pack(pop)

/**
 * Struct for storing parsed generic response data
 */
#pragma pack(push, 1)
typedef struct {
	uint16	rval;
	uint8	status;
} GenericResponse;
#pragma pack(pop)

/**
 * Struct for storing parsed system info
 */
#pragma pack(push, 1)
typedef struct {
	uint16	rval;
	uint8	status;
	uint8	deviceName[15];			//not null terminated
	uint8	btMAC[6];				//not null terminated
	uint32	btStrength;
	uint32	flash;
} DeviceSystemInfo;
#pragma pack(pop)

/**
 * Struct for storing response from the digital I2C sensor
 */
#pragma pack(push, 1)
typedef struct {
	uint16	rval;
	uint8	status;
	uint8	length;
	uint8	data[16];
} DigitalSensorReadRespose;
#pragma pack(pop)

/**
 * Struct for storing status of the digital I2C sensor
 */
#pragma pack(push, 1)
typedef struct {
	uint16	rval;
	uint8	status;
	uint8	bytesReady;
} DigitalSensorStatusRespose;
#pragma pack(pop)


/**
 * Parse NXT response into GenericResponse struct
 *
 * @return	1 - if input buffer is too short
 * 			2 - if GenericResponse pointer is NULL
 * 			In other cases returns value of parsed 'status' field
 *
 */
int8 nxtParseGenericResponse(uint8 *responseBuffer, uint8 responseBufferLength, GenericResponse *response);

/**
 * Parse NXT response on 'nxtGetBatteryLevel' request into BatteryState struct
 *
 * @return	1 - if input buffer is too short
 * 			2 - if BatteryState pointer is NULL
 * 			In other cases returns value of parsed 'status' field
 *
 */
int8 nxtParseBatteryStatus(uint8 *responseBuffer, uint8 responseBufferLength, BatteryState *state);

/**
 * Parse NXT response on 'nxtGetMotorState' request into MotorOutputState struct
 *
 * @return	1 - if input buffer is too short
 * 			2 - if MotorOutputState pointer is NULL
 * 			In other cases returns value of parsed 'status' field
 *
 */
int8 nxtParseMotorOutput(uint8 *responseBuffer, uint8 responseBufferLength, MotorOutputState *output);

/**
 * Parse NXT response on 'nxtGetInputValues' request into SensorState struct
 *
 * @return	1 - if input buffer is too short
 * 			2 - if SensorState pointer is NULL
 * 			In other cases returns value of parsed 'status' field
 *
 */
int8 nxtParseSensorState(uint8 *responseBufer, uint8 responseBufferLength, SensorState* state);

/**
 * Parse NXT response on 'nxtGetDeviceSystemInfo' request into DeviceSystemInfo struct
 *
 * @return	1 - if input buffer is too short
 * 			2 - if DeviceSystemInfo pointer is NULL
 * 			In other cases returns value of parsed 'status' field
 *
 */
int8 nxtParseDeviceSystemInfo(uint8 *responseBuffer, uint8 responseBufferLength, DeviceSystemInfo *info);

/**
 * Parse NXT response on 'nxtLSGetStatus' request into DigitalSensorStatusResponse struct
 *
 * @return	1 - if input buffer is too short
 * 			2 - if DigitalSensorStatusRespose pointer is NULL
 * 			In other cases returns value of parsed 'status' field
 *
 */
int8 nxtParseDigitalSersorStatus(uint8 *responseBuffer, uint8 responseBufferLength, DigitalSensorStatusRespose *response);

/**
 * Parse NXT response on 'nxtLSRead' request into DigitalSensorReadResponse struct
 *
 * @return	1 - if input buffer is too short
 * 			2 - if DigitalSensorReadRespose pointer is NULL
 * 			In other cases returns value of parsed 'status' field
 *
 */
int8 nxtParseDigitalSersorRead(uint8 *responseBuffer, uint8 responseBufferLength, DigitalSensorReadRespose *response);

/**
 * Send 'KeepAlive' packet to the NXT to prevent device sleeping
 *
 * @param deviceHandle
 * @param wantResponse		Flag if response from the device is required
 * @param response			GenericResponse object to store response data
 *
 * @return 	0 						- if everything is OK
 * 			1 						- if received response buffer is too short
 * 			4 						- if presented deviceHandle is NULL
 * 			5 						- if GenericResponse pointer is NULL but response is required
 * 			any other value < 0 	- look at libusb error codes
 * 			any other value > 0		- use 'nxtGetStatusDescription' to get readable error description
 *
 */
int8 nxtKeepAlive(libusb_device_handle **deviceHandle, uint8 wantResponse, GenericResponse *response);

/**
 * Play sound with specified tone and duration
 *
 * @param deviceHandle
 * @param tone				Tone of the sound
 * @param duration			Duration of the sound (in milliseconds)
 * @param wantResponse		Flag if response from the device is required
 * @param response			GenericResponse object to store response data
 *
 * @return 	0 						- if everything is OK
 * 			1 						- if received response buffer is too short
 * 			4 						- if presented deviceHandle is NULL
 * 			5 						- if GenericResponse pointer is NULL but response is required
 * 			any other value < 0 	- look at libusb error codes
 * 			any other value > 0		- use 'nxtGetStatusDescription' to get readable error description
 *
 */
int8 nxtPlaySound(libusb_device_handle **deviceHandle, uint16 tone, uint16 duration, uint8 wantResponse, GenericResponse *response);

/**
 * Start a program with the specified filename on the NXT brick
 *
 * @param deviceHandle
 * @param file				Name of the program to start
 *
 * @return 	0 						- if everything is OK
 * 			1 						- if received response buffer is too short
 * 			4 						- if presented deviceHandle is NULL
 * 			5 						- if GenericResponse pointer is NULL but response is required
 * 			any other value < 0 	- look at libusb error codes
 * 			any other value > 0		- use 'nxtGetStatusDescription' to get readable error description
 *
 */
int8 nxtStartProgram(libusb_device_handle **deviceHandle, uint8 *file);

/**
 * Request battery level from the NXT device
 *
 * @param deviceHandle
 * @param battery			BatteryState object to store parsed response data
 *
 * @return 	0 						- if everything is OK
 * 			1 						- if received response buffer is too short
 * 			4 						- if presented deviceHandle is NULL
 * 			5 						- if BatteryState pointer is NULL
 * 			any other value < 0 	- look at libusb error codes
 * 			any other value > 0		- use 'nxtGetStatusDescription' to get readable error description
 *
 */
int8 nxtGetBatteryLevel(libusb_device_handle **deviceHandle, BatteryState *battery);

/**
 * Set the output state for the specified motor port
 *
 * @param deviceHandle
 * @param motorPort			Port of the motor to control
 * @param power				Power level between [-100; 100]
 * @param mode				Bitwise OR of possible output modes (look at 'nxtconst.h')
 * @param regulationMode	One of the motor regulation mode constants
 * @param turnRatio
 * @param runstateMode		One of the motor runstates constant
 * @param tachoLimit		Number of rotation ticks the motor should turn before it stops
 * @param wantResponse		Flag if response from the device is required
 * @param response			GenericResponse object to store response data (if you want to get the state of the motor then use 'nxtGetMotorState')
 *
 * @return 	0 						- if everything is OK
 * 			1 						- if received response buffer is too short
 * 			4 						- if presented deviceHandle is NULL
 * 			5 						- if GenericResponse pointer is NULL but response required
 * 			any other value < 0 	- look at libusb error codes
 * 			any other value > 0		- use 'nxtGetStatusDescription' to get readable error description
 *
 */
int8 nxtMotorsRotate(libusb_device_handle **deviceHandle, uint8 motorPort, int8 power, uint8 mode, uint8 regulationMode, int8 turnRatio, uint8 runstateMode, uint32 tachoLimit, uint8 wantResponse, GenericResponse *response);

/**
 * Request the state of the motor on the specified port
 *
 * @param deviceHandle
 * @param motorPort			Port of the motor which state should be returned
 * @param motorState		MotorOutputState object to store parsed response data
 *
 * @return	0 						- if everything is OK
 * 			1 						- if received response buffer is too short
 * 			4 						- if presented deviceHandle is NULL
 * 			5 						- if MotorOutputState pointer is NULL
 * 			any other value < 0 	- look at libusb error codes
 * 			any other value > 0		- use 'nxtGetStatusDescription' to get readable error description
 *
 */
int8 nxtGetMotorState(libusb_device_handle **deviceHandle, uint8 motorPort, MotorOutputState *motorState);

/**
 * Request values from the sensor on the specified port.
 * Remember that some sensors must be configured with
 * additional commands(like ultrasonic sensor e.g.).
 *
 * @param deviceHandle
 * @param port				Port of the sensor which data should be requested
 * @param sensorState		SensorState object to store parsed response data
 *
 * @return	0 						- if everything is OK
 * 			1 						- if received response buffer is too short
 * 			4 						- if presented deviceHandle is NULL
 * 			5 						- if SensorState pointer is NULL
 * 			any other value < 0 	- look at libusb error codes
 * 			any other value > 0		- use 'nxtGetStatusDescription' to get readable error description
 *
 */
int8 nxtGetInputValues(libusb_device_handle **deviceHandle, uint8 port, SensorState *sensorState);

/**
 * Request system info from the NXT
 *
 * @param deviceHandle
 * @param info				DeviceSystemInfo object to store parsed response data
 *
 * @return	0 						- if everything is OK
 * 			1 						- if received response buffer is too short
 * 			4 						- if presented deviceHandle is NULL
 * 			5 						- if DeviceSystemInfo pointer is NULL
 * 			any other value < 0 	- look at libusb error codes
 * 			any other value > 0		- use 'nxtGetStatusDescription' to get readable error description
 *
 */
int8 nxtGetDeviceSystemInfo(libusb_device_handle **deviceHandle, DeviceSystemInfo *info);

/**
 * Get text description of the status code received from the NXT
 *
 * @return Text description if status code is recognized. Empty string if status code not found.
 *
 */
char* nxtGetStatusDescription(uint8 nxtStatus);

/**
 * Configure the input mode of a sensor port.
 *
 * @param deviceHandle
 * @param port				port to configure
 * @param sensorType		type of the sensor
 * @param sensorMode		sensor mode
 * @param wantResponse		flag if response from device is required
 * @param response			GenericResponse object
 *
 * @return 	0 						- if everything is OK
 * 			1 						- if received response buffer is too short
 * 			4 						- if presented deviceHandle is NULL
 * 			5 						- if DeviceSystemInfo pointer is NULL
 * 			any other value < 0 	- look at libusb error codes
 * 			any other value > 0		- use 'nxtGetStatusDescription' to get readable error description
 *
 */
int8 nxtSetInputMode(libusb_device_handle **deviceHandle, uint8 port, uint8 sensorType, uint8 sensorMode, uint8 wantResponse, GenericResponse *response);

/**
 * Determine whether there is data ready to read from an I2C digital sensor
 *
 * @param deviceHandle
 * @param port				port to configure
 * @param wantResponse		flag if response from device is required
 * @param response			GenericResponse object
 */
int8 nxtLSGetStatus(libusb_device_handle **deviceHandle, uint8 port, uint8 wantResponse, DigitalSensorStatusRespose *response);

/**
 * Send a command to I2C digial sensor
 *
 * @param deviceHandle
 * @param port				port to configure
 * @param txlen				length of the 'txdata' buffer to send to the device
 * @param rxlen				how many bytes should be returned by the I2C sensor
 * @param txdata			data buffer to send to the device
 * @param wantResponse		flag if response from device is required
 * @param response			GenericResponse object
 *
 */
int8 nxtLSWrite(libusb_device_handle **deviceHandle, uint8 port, uint8 txlen, uint8 rxlen, uint8 *txdata, uint8 wantResponse, GenericResponse *response);

/**
 * Read data from I2C digital sensor
 *
 * @param deviceHandle
 * @param port				port to configure
 * @param response			DigitalSensorReadResponse object to store response from the device
 *
 */
int8 nxtLSRead(libusb_device_handle **deviceHandle, uint8 port, DigitalSensorReadRespose *response);

/**
 * Loops to ensure data is ready to read from the sensor and returns the result.
 *
 * @param deviceHandle
 * @param port				port to configure
 * @param txlen				length of the 'txdata' buffer to send to the device
 * @param rxlen				how many bytes should be returned by the I2C sensor
 * @param txdata			data buffer to send to the device
 * @param wantResponse		flag if response from device is required
 * @param response			DigitalSensorReadRespose object to store response from the device
 *
 */
int8 nxtLSRequestResponse(libusb_device_handle **deviceHandle, uint8 port, uint8 txlen, uint8 rxlen, uint8 *data, DigitalSensorReadRespose *readResponse);

#endif /* NXT_H_ */
